package experiment;

import heuristics.AmbitiousStudentHeuristic;
import heuristics.IHeuristic;
import heuristics.LazyStudentHeuristic;
import heuristics.WorkingStudentHeuristic;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import log.Log;
import Entities.Calendar;
import Entities.CoursesConfiguration;
import algorithms.RandomWalkHC;
import algorithms.TYPE_OF_RANDOM_WALK;

public class RandomWalkExperiment extends BaseExperiment {
	private static RandomWalkHC randomWalk;
	private static int MAX_HEURISTIC_CALLS = 50000; // limited heuristic calls number
	private static int[] FIXED_WALK_VALUES = { 1, 3, 5, 8, 10, 15, 20};
	private static double[] FACTOR_WALK_VALUES = { 0.05, 0.1, 0.2, 0.3, 0.4 };
	private List<IHeuristic> heuristics;
	private Log log;
	
	public RandomWalkExperiment()
	{
		heuristics = new LinkedList<IHeuristic>(); 
		heuristics.add(new LazyStudentHeuristic());
		heuristics.add(new AmbitiousStudentHeuristic());
		heuristics.add(new WorkingStudentHeuristic());
		try {
			setCourseConfigurations("coursesConfigurations");
			log = new Log("experiments_output/RandomWalkExperiment2.csv");
			log.writeLabels(new String[]{"Heuristic", "Walk Constant", "Average Heuristic value", "Average steps to best value"});
		} catch (IOException e) {
			System.out.println("Experiment failed");
			e.printStackTrace();
		}	
	}
	
	@Override
	public void Execute() 
	{
		System.out.println("Experiment Started: " + getDateTime());// TODO: debug
		randomWalk = new RandomWalkHC();
		randomWalk.setMaxOfHeuristicCalls(MAX_HEURISTIC_CALLS);
		for (IHeuristic heuristic : heuristics){
			Calendar.setHeuristic(heuristic);
			addHeuristicToLog(heuristic);
			for (TYPE_OF_RANDOM_WALK type : TYPE_OF_RANDOM_WALK.values()) // fixed of factor 
			{
				if (type == TYPE_OF_RANDOM_WALK.FIXED) 
				{
					RandomWalkHC.setTypeOfRandomWalk(type);
					for (int fixedWalkValue = 0; fixedWalkValue < FIXED_WALK_VALUES.length; fixedWalkValue++) 
					{
						RandomWalkHC.setFixedNumberOfRandomWalk(FIXED_WALK_VALUES[fixedWalkValue]);
						RunExperiment();
					}
				}
				if (type == TYPE_OF_RANDOM_WALK.FACTOR) {
					RandomWalkHC.setTypeOfRandomWalk(type);
					for (int factorWalkValue = 0; factorWalkValue < FACTOR_WALK_VALUES.length; factorWalkValue++) {
						RandomWalkHC.setRandomWalkFactor(FACTOR_WALK_VALUES[factorWalkValue]);
						RunExperiment();
					}
				}
			}
		}
		System.out.println("Experiment Ended: " + getDateTime());
		try {
			log.close();
		} catch (IOException e) {
			System.err.println("Log wasn't closed");
			e.printStackTrace();
		}
	}
	
	private void addHeuristicToLog(IHeuristic heuristic) {
		HashMap<String, String> data = new HashMap<String, String>();
		data.put("Heuristic", heuristic.toString());
		try {
			log.writeData(data);
		} catch (IOException e) {
			System.err.println("Couldn't write heuristic to Log");
			e.printStackTrace();
		}
	}
	
	private void RunExperiment() {
		Calendar res; 
		double averageHeuristicValue = 0, averageNumberOfHeuristicCallsToBest = 0;
		for (CoursesConfiguration possibleCourses : courseConfigurations)
		{
			Calendar.init(possibleCourses.getCoursesConfiguration());
			for (int runNumber = 0; runNumber < NUMBER_OF_REPETED_EXECUTIONS; runNumber++) 
			{
				randomWalk.reset();
				res = randomWalk.searchLocal(); // run with empty calendar					
				averageHeuristicValue += res.getHeuristicValue();
				averageNumberOfHeuristicCallsToBest += randomWalk.getNumberOfHeuristicCallsToBest();
			}
		}
		averageHeuristicValue /= (double) (NUMBER_OF_REPETED_EXECUTIONS*courseConfigurations.size());
		averageNumberOfHeuristicCallsToBest /= (double) (NUMBER_OF_REPETED_EXECUTIONS*courseConfigurations.size());
		PrintExecutionResult(averageHeuristicValue, averageNumberOfHeuristicCallsToBest);
	}

	private void PrintExecutionResult(double heuristicAverage, double numberOfStepAverage) {
		HashMap<String, String> data = new HashMap<String, String>();
		if (RandomWalkHC.getTypeOfRandomWalk() == TYPE_OF_RANDOM_WALK.FIXED){
			data.put("Walk Constant", "Fixed value of: " + String.valueOf(RandomWalkHC.getFixedNumberOfRandomWalk()));			
		}
		else if (RandomWalkHC.getTypeOfRandomWalk() == TYPE_OF_RANDOM_WALK.FACTOR){
			data.put("Walk Constant", "Factor of: " + String.valueOf(RandomWalkHC.getRandomWalkFactor()));
		}
		data.put("Average Heuristic value", String.valueOf(heuristicAverage));
		data.put("Average steps to best value", String.valueOf(numberOfStepAverage));
		for (String type : data.keySet()) { // TODO: debug
			System.out.println(type + " " + data.get(type));
		}
		try {
			log.writeData(data);
		} catch (IOException e) {
			System.out.println("In Print ExecutionResult");
			e.printStackTrace();
		}
	}
}
